package kuti.grp5;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Random;
import kuti.*;

/**
 *
 * @author Jonathan & Pierre
 */
public class Kuti implements IKuti, IObservableKuti, IRadarElement {


    /*
     * Id to identify the kuti
     */
    private static int next_id = 0;
    /*
     * Id of the kuti
     */
    private int id;
    /*
     * State of the kuti
     */
    private EKutiState state;
    /*
     * Boolean to know if a kuti is active or not
     */
    private boolean active;
    /*
     * ECutterState to know the state of the cutter
     */
    private ECutterState cutterState;
    /*
     * Point that defines the position of a kuti
     */
    private Point2D centerPosition;
    /*
     * The current orientation of the kuti
     */
    private double orientation;
    /*
     * The current speed of the kuti
     */
    private double speed = 0.0;
    /*
     * The driver that drives the kuti
     */
    private IDriver driver;
    /*
     * The radar of the kuti
     */
    private IRadar radar;

    /*
     * A boolean to know if the kuti is colliding or not
     */
    private boolean colliding;
    /*
     * The distance that the kuti has traveled (each tick)
     */
    private double traveledDistance = 0;
    /*
     * The kuti actuator
     */
    private IActuator actuator;
    /*
     * The parameters of the simulation
     */
    private ISimulParams simulParams;
    
    /*
     * TODO REMOVE?
     */
    private IWorldManager worldManager;

    /*
     * The energy manager of the kuti
     */
    private EnergyManager energyManager;
    /*
     * The action manager of the kuti
     */
    private ActionManager actionManager;
    /*
     * The collision manager of the kuti
     */
    private CollisionManager collisionManager;
    /*
     * The cutter manager of the kuti
     */
    private CutterManager cutterManager;

    /**
     * Constructor of the class Kuti
     * 
     * @param cutterState state of the cutter
     * @param centerPosition the start position of the kuti
     * @param orientation the start orientation of the kuti
     * @param driver the driver that will drive the kuti
     * @param radar the radar of the kuti
     * @param energyLevel the start energy level of the kuti
     * @param simulParams the parameters of the simulation
     */
    public Kuti(ECutterState cutterState, Point2D centerPosition,
            double orientation, IDriver driver, IRadar radar, double energyLevel,
            ISimulParams simulParams) {
        
        if(centerPosition == null){
            throw new IllegalArgumentException("The start position of the kuti is not defined");
        }
        if(Double.isNaN(orientation)){
            throw new IllegalArgumentException("The start orientation is not a number");
        }
        if(driver == null){
            throw new IllegalArgumentException("The driver is not defined");
        }
        if(radar == null){
            throw new IllegalArgumentException("The radar is not defined");
        }
        if(simulParams == null){
            throw new IllegalArgumentException("The simulation parameters ar not defined");
        }
        
        //TODO EnergyLevel ? remove?
        this.id = next_id++;
        state = EKutiState.STOPPED;
        active = true;
        this.cutterState = cutterState;
        this.centerPosition = centerPosition;
        this.orientation = orientation;
        this.driver = (Driver) driver;
        this.radar = radar;
        //this.energyLevel = energyLevel;
        colliding = false;
        this.simulParams = simulParams;

        cutterManager = new CutterManager(this,simulParams);

        collisionManager = new CollisionManager(this);

        energyManager = new EnergyManager(this, simulParams, energyLevel);
        energyManager.equilibrate(energyLevel);

        actionManager = new ActionManager(this, simulParams);

    }

    public void setWorldManager(IWorldManager worldManager) {
        this.worldManager = worldManager;
    }

    public IWorldManager getWorldManager() {
        return worldManager;
    }

    /**
     * 
     * @return the current position of the kuti
     */
    @Override
    public Point2D getCenterPosition() {
        return centerPosition;
    }

    /**
     * 
     * @return the current state of the kuti
     */
    @Override
    public EKutiState getState() {
        return state;
    }

    /**
     * 
     * @return the current state of the cutter 
     */
    @Override
    public ECutterState getCutterState() {
        return cutterState;
    }

    /**
     * 
     * @return the current orientation of the kuti
     */
    @Override
    public double getOrientation() {
        return orientation;
    }

    /**
     * 
     * @return the current speed of the kuti
     */
    @Override
    public double getSpeed() {
        return speed;
    }

    /**
     * 
     * @return true if the kuti is colliding else false
     */
    @Override
    public boolean isColliding() {
        return colliding;
    }
    /**
     * 
     * @return the traveled distance after the last tick
     */
    @Override
    public double traveledDistance() {
        return traveledDistance;
    }

    /**
     * 
     * @return the driver of the kuti
     */
    @Override
    public IDriver getDriver() {
        return driver;
    }

    /**
     * 
     * @return the radar of the kuti
     */
    @Override
    public IRadar getRadar() {
        return radar;
    }

    /**
     * 
     * @return the id of the kuti 
     */
    @Override
    public int getId() {
        return id;
    }

    /**
     * 
     * @return true if the kuti is active else false
     */
    @Override
    public boolean isActive() {
        return active;
    }
    
    /**
     * 
     * @return the current energy level of the kuti
     */
    @Override
    public double getEnergyLevel() {
        return energyManager.getEnergyLevel();
    }

    /**
     * 
     * @return the element KUTI
     */
    @Override
    public EWorldElementType getElementType() {
        return EWorldElementType.KUTI;
    }

    /**
     * This method is used to set the speed
     * @param speed 
     */
    public void setSpeed(double speed) {
        this.speed = speed;
    }

    /**
     * This method is used to set the active to true or false
     * @param active 
     */
    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     * This method is used to set to true the boolean if the kuti is colliding
     * @param colliding 
     */
    public void setColliding(boolean colliding) {
        this.colliding = colliding;
    }

    /**
     * This method is used to set the state of the cutter
     * @param cutterState 
     */
    public void setCutterState(ECutterState cutterState) {
        this.cutterState = cutterState;
    }

    /**
     * This method is used to set the orientation, the orientation
     * must be between -pi to +pi, then this method transforms the orientation
     * in the 0-2pi format.
     * @param orientation 
     */
    public void setOrientation(double orientation) {
        this.orientation += orientation;
        while (this.orientation < 0) {
            this.orientation += (2 * Math.PI);
        }
        this.orientation %= (2 * Math.PI);
    }

    /**
     * This method is used to set the position of the kuti
     * @param centerPosition 
     */
    public void setCenterPosition(Point2D centerPosition) {
        this.centerPosition = centerPosition;
    }

    /**
     * This method is used to set the state of the kuti
     * @param state 
     */
    public void setState(EKutiState state) {
        this.state = state;
    }

    /**
     * This method is used to set the traveledDistance of the kuti
     * @param traveledDistance 
     */
    public void setTraveledDistance(double traveledDistance) {
        this.traveledDistance = traveledDistance;
    }

    /**
     * This method is used to set the actuator 
     * @param actuator 
     */
    public void setActuator(IActuator actuator) {
        this.actuator = actuator;
    }
    /**
     * 
     * @return the actuator of the kuti
     */
    public IActuator getActuator() {
        return this.actuator;
    }

    @Override
    public boolean isIntersects(Line2D object) {
        return (object.ptSegDist(centerPosition) < simulParams.getKDiameter() / 2);
    }

    @Override
    public double intersectsDistance(Line2D kuti) {

        // Create a vector of maximal kuti movement
        Vector2D kutiMov = new Vector2D(kuti.getP1(), kuti.getP2());

        // Create a vector from first kuti to other one. 
        Vector2D kutiDist = new Vector2D(kuti.getP1(), this.getCenterPosition());

        // Create a projection from the vector between the kutis and the kuti movement vector
        Vector2D projection = Vector2D.projection(kutiMov, kutiDist);

        // Create a vector between the other kuti and the final kuti point
        Vector2D closest = projection.clone();
        closest.sub(kutiDist);
        closest.invert();

        // Calculate the delta
        double delta = Math.sqrt((simulParams.getKDiameter() / 2) * (simulParams.getKDiameter() / 2) - closest.length() * closest.length());

        // Create the intersection point
        Point2D intersect = KutiTrajectory.moveTo(kuti.getP1(), projection.length() - delta, orientation);

        return intersect.distance(kuti.getP1());

    }

    /**
     * 
     * @return the energy manager of the kuti
     */
    public EnergyManager getEnergyManager() {
        return energyManager;
    }

    /**
     * 
     * @return the action manager of the kuti
     */
    public ActionManager getActionManager() {
        return actionManager;
    }

    /**
     * 
     * @return the collision manager of the kuti
     */
    public CollisionManager getCollisionManager() {
        return collisionManager;
    }
    /**
     * 
     * @return the cutter manager of the kuti
     */
    public CutterManager getCutterManager(){
        return cutterManager;
    }
}
